﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net;
using System.ServiceModel.Web;
using Microsoft.Samples.WindowsAzure.ServiceManagement;
using System.Security.Cryptography.X509Certificates;
using Microsoft.WindowsAzure.ServiceRuntime;
using System.ServiceModel;
using Microsoft.WindowsAzure.StorageClient;
using Microsoft.WindowsAzure;
using System.Security.Cryptography;
using System.Management.Automation;
using System.IO;
using System.Reflection;
using System.Collections.ObjectModel;
using System.Threading;

namespace WAPA.Framework
{
    //public static partial class Extensions
    //{
    //    private static readonly Random _random = new Random();

    //    public static void ObservableAdd(this ObservableCollection<OperationContext> list, OperationContext responseInfo)
    //    {
    //        list.Add(responseInfo);

    //        Thread.Sleep(_random.Next(200, 600));
    //    }
    //}


    /// <summary>
    /// Wraps and extends the ServiceManagement library for making calls to the Windows Azure Mangagement REST API.
    /// One of the extensions is the responsibility to base64 encode the appropriate request fields so these API details are transparant to the callers.
    /// </summary>
    public class CloudServiceManager
    {
        //TODO: Wait for operation status response using the operation ID in de request response.
        //TODO: Fault tolerance and sync of activities.
        //TODO: Clean all artifacts before re-initialize.




        /// <summary>
        /// Setup the communication channel to the API and provide the credentials needed.
        /// </summary>
        /// <param name="endpointConfigurationName"></param>
        /// <param name="cert"></param>
        /// <returns></returns>
        public static IServiceManagement CreateServiceManagementChannel(string endpointConfigurationName, X509Certificate2 cert)
        {
            WebChannelFactory<IServiceManagement> factory = new WebChannelFactory<IServiceManagement>(endpointConfigurationName);
            factory.Endpoint.Behaviors.Add(new ClientOutputMessageInspector());
            factory.Credentials.ClientCertificate.Certificate = cert;

            var channel = factory.CreateChannel();
            return channel;
        }

        public static List<CloudBlob> GetBlobs(string containerName)
        {
            CloudBlobClient blobClient = new CloudBlobClient(ManagementContext.BlobEndpointUri, new StorageCredentialsAccountAndKey(ManagementContext.StorageAccountName, ManagementContext.AccessKey));

            try
            {
                //Get a reference to a container, which may or may not exist.
                CloudBlobContainer container = blobClient.GetContainerReference(containerName.ToLower());

                var blobs = container.ListBlobs();

                List<CloudBlob> blobList = new List<CloudBlob>();
                foreach (IListBlobItem blobItem in blobs)
                {
                    blobList.Add(blobItem as CloudBlob);
                }

                return blobList;
            }
            catch
            { }

            return null;
        }

        /// <summary>
        /// Get strongly types messages from a queue
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="queueName"></param>
        /// <returns></returns>
        public static List<T> GetQueueMessages<T>(string queueName)
        {
            CloudQueueClient queueClient = new CloudQueueClient(ManagementContext.QueueEndpointUri, new StorageCredentialsAccountAndKey(ManagementContext.StorageAccountName, ManagementContext.AccessKey));

            try
            {
                //Get a reference to a queue in this storage account.
                CloudQueue queue = queueClient.GetQueueReference(queueName.ToLower());

                //Check whether the queue exists, and create it if it does not.
                if (queue.Exists())
                {
                    IEnumerable<CloudQueueMessage> messageList = queue.PeekMessages(32);

                    List<T> resultList = new List<T>();

                    foreach (CloudQueueMessage message in messageList)
                    {
                        resultList.Add(MessageSerializer.DeserializeMessage<T>(message.AsString));
                    }

                    return resultList;
                }
                else
                {
                    return default(List<T>);
                }
            }
            catch { }

            return null;
        }


        /// <summary>
        /// Accepts a certificate thumbprint and checks if this machine has the corresponding certificate in its certificate store.
        /// </summary>
        /// <param name="certThumbprint"></param>
        /// <returns></returns>
        public static X509Certificate2 GetCertificate(string certThumbprint)
        {
            if (String.IsNullOrEmpty(certThumbprint)) return null;

            X509Store x509Store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            x509Store.Open(OpenFlags.ReadOnly);
            X509Certificate2Collection certs = x509Store.Certificates.Find(X509FindType.FindByThumbprint, certThumbprint, false);
            if (certs.Count != 1) return null;

            return certs[0];
        }

        /// <summary>
        /// Collect all currently available OS versions.
        /// </summary>
        /// <returns></returns>
        public string RetrieveOsVersions()
        {
            string errorMessage = null;
            OperatingSystemList osList = null;
            string returnMessage = string.Empty;

            X509Certificate2 certificate = GetCertificate(ManagementContext.CertificateThumbprint);
            if (certificate == null)
            {
                return errorMessage;
            }

            var serviceManagement = ServiceManagementHelper.CreateServiceManagementChannel("WindowsAzureEndPoint", certificate);

            try
            {
                string trackingId = null;
                HttpStatusCode? statusCode = null;
                string statusDescription = null;

                using (OperationContextScope scope = new OperationContextScope((IContextChannel)serviceManagement))
                {
                    try
                    {
                        osList = serviceManagement.ListOperatingSystems(ManagementContext.SubscriptionId);
                        if (WebOperationContext.Current.IncomingResponse != null)
                        {
                            trackingId = WebOperationContext.Current.IncomingResponse.Headers[Constants.OperationTrackingIdHeader];
                            statusCode = WebOperationContext.Current.IncomingResponse.StatusCode;
                            statusDescription = WebOperationContext.Current.IncomingResponse.StatusDescription;
                            returnMessage += string.Format("Operation ID: {0}", trackingId);
                        }
                    }
                    catch (CommunicationException ce)
                    {
                        ServiceManagementError error = null;
                        HttpStatusCode httpStatusCode = 0;
                        string operationId;
                        ServiceManagementHelper.TryGetExceptionDetails(ce, out error, out httpStatusCode, out operationId);
                        if (error == null)
                        {
                            returnMessage += string.Format(ce.Message);
                        }
                        else
                        {
                            returnMessage += string.Format("HTTP Status Code: {0}", httpStatusCode);
                            returnMessage += string.Format("Error Message: {0}", error.Message);
                            returnMessage += string.Format("Operation Id: {0}", operationId);
                        }
                    }
                    finally
                    {
                        if (statusCode != null)
                        {
                            returnMessage += string.Format("HTTP Status Code: {0}", statusCode);
                            returnMessage += string.Format("StatusDescription: {0}", statusDescription);
                        }
                    }
                }
                /*
                if (trackingId != null && statusCode != null && statusCode == HttpStatusCode.Accepted)
                {
                    Console.WriteLine("Waiting for async operation to complete:");
                    WaitForAsyncOperation(serviceManagement, trackingId);
                }
                */
            }
            catch (TimeoutException)
            {
                returnMessage += string.Format("There was an error processing this command.");
            }
            if (osList == null)
            {
                returnMessage += "OS list is null. Please try again.";
            }
            else
            {
                returnMessage += "\r\n";
                returnMessage += string.Join("\r\n",
                    (from os in osList select string.Format("OS name: {0} OS label: {1} IsActive: {2} IsDefault: {3}", os.Version, os.Label, os.IsActive, os.IsDefault)).ToArray());
            }
            return returnMessage;
        }

        /// <summary>
        /// Gets all hosted services related to the provided subscription id.
        /// </summary>
        public static HostedServiceList GetHostedServices()
        {
            OperationContext operationContext = new OperationContext("List hosted services");
            ManagementContext.AddOperationContext(operationContext);

            var serviceManagementChannel = ServiceManagementHelper.CreateServiceManagementChannel("WindowsAzureEndPoint", ManagementContext.certificate);

            HostedServiceList hostedServiceList = null;

            try
            {
                using (OperationContextScope scope = new OperationContextScope((IContextChannel)serviceManagementChannel))
                {
                    try
                    {
                        hostedServiceList = serviceManagementChannel.ListHostedServices(ManagementContext.SubscriptionId);
                        if (WebOperationContext.Current.IncomingResponse != null)
                        {
                            operationContext.AddWebOperationContext(WebOperationContext.Current);
                        }
                    }
                    catch (CommunicationException ce)
                    {
                        HandleCommunicationError(ce);
                    }
                }
            }
            catch (TimeoutException)
            {

            }

            return hostedServiceList;
        }


        /// <summary>
        /// Get a hosted service info object for a specified service name.
        /// </summary>
        /// <returns></returns>
        public static HostedService GetHostedService(string serviceName)
        {
            OperationContext operationContext = new OperationContext("Get hosted service named " + serviceName);
            ManagementContext.AddOperationContext(operationContext);

            var serviceManagementChannel = ServiceManagementHelper.CreateServiceManagementChannel("WindowsAzureEndPoint", ManagementContext.certificate);

            HostedService hostedService = null;

            try
            {
                using (OperationContextScope scope = new OperationContextScope((IContextChannel)serviceManagementChannel))
                {

                    try
                    {
                        hostedService = serviceManagementChannel.GetHostedService(ManagementContext.SubscriptionId, serviceName);
                        if (WebOperationContext.Current.IncomingResponse != null)
                        {
                            operationContext.AddWebOperationContext(WebOperationContext.Current);
                        }
                    }
                    catch (CommunicationException ce)
                    {
                        HandleCommunicationError(ce);
                    }
                }
            }
            catch (TimeoutException)
            {

            }

            return hostedService;
        }

        /// <summary>
        /// Get a hosted service info object for a specified service label.
        /// </summary>
        /// <returns></returns>
        public static HostedServiceList GetHostedServicesByLabel(string serviceLabel)
        {
            OperationContext operationContext = new OperationContext("Get hosted service list");
            ManagementContext.AddOperationContext(operationContext);
            var serviceManagementChannel = ServiceManagementHelper.CreateServiceManagementChannel("WindowsAzureEndPoint", ManagementContext.certificate);

            HostedServiceList hostedServiceList = null;

            try
            {

                using (OperationContextScope scope = new OperationContextScope((IContextChannel)serviceManagementChannel))
                {
                    try
                    {
                        hostedServiceList = serviceManagementChannel.ListHostedServices(ManagementContext.SubscriptionId);
                        if (WebOperationContext.Current.IncomingResponse != null)
                        {
                            operationContext.AddWebOperationContext(WebOperationContext.Current);
                        }
                    }
                    catch (CommunicationException ce)
                    {
                        HandleCommunicationError(ce);
                    }
                }
            }
            catch (TimeoutException)
            {

            }

            if (hostedServiceList == null || hostedServiceList.Count == 0) return null;

            HostedServiceList matchedHostedServices = new HostedServiceList();
            foreach (HostedService hostedService in hostedServiceList)
            {
                HostedService hostedServiceWithDetails = GetHostedServiceWithDetails(hostedService.ServiceName);

                if (hostedServiceWithDetails.HostedServiceProperties.Label == serviceLabel)
                {
                    matchedHostedServices.Add(hostedServiceWithDetails);
                }
            }

            return matchedHostedServices;
        }

        /// <summary>
        /// Create a queue using the specifief queue name.
        /// </summary>
        /// <param name="queueName"></param>
        public static void SetupQueue(string queueName)
        {
            CloudQueueClient queueClient = new CloudQueueClient(ManagementContext.QueueEndpointUri, new StorageCredentialsAccountAndKey(ManagementContext.StorageAccountName, ManagementContext.AccessKey));

            //Get a reference to a queue in this storage account.
            CloudQueue queue = queueClient.GetQueueReference(queueName.ToLower());

            //Check whether the queue exists, and create it if it does not.
            if (!queue.Exists())
            {
                queue.Create();
            }
            else
            {
                //queue.Clear();
            }
        }

        /// <summary>
        /// Delete a queue using the specifief queue name.
        /// </summary>
        /// <param name="queueName"></param>
        public static void DeleteQueue(string queueName)
        {
            CloudQueueClient queueClient = new CloudQueueClient(ManagementContext.QueueEndpointUri, new StorageCredentialsAccountAndKey(ManagementContext.StorageAccountName, ManagementContext.AccessKey));
            //Get a reference to a queue in this storage account.
            CloudQueue queue = queueClient.GetQueueReference(queueName.ToLower());

            if (queue.Exists())
            {
                queue.Delete();
            }

        }

        /// <summary>
        /// Add a message to a specified queue
        /// </summary>
        /// <param name="queueName"></param>
        /// <param name="serializedMessageContent"></param>
        public static void SendQueueMessage(string queueName, string serializedMessageContent)
        {
            CloudQueueClient queueClient = new CloudQueueClient(ManagementContext.QueueEndpointUri, new StorageCredentialsAccountAndKey(ManagementContext.StorageAccountName, ManagementContext.AccessKey));

            //Get a reference to a queue in this storage account.
            CloudQueue queue = queueClient.GetQueueReference(queueName.ToLower());

            //Check whether the queue exists, and create it if it does not.
            if (!queue.Exists())
            {
                queue.Create();
            }

            CloudQueueMessage cloudQueueMessage = new CloudQueueMessage(serializedMessageContent);

            //Add the messages to the queue.
            queue.AddMessage(cloudQueueMessage);
        }

        public static HostedServiceList GetHostedServicesWithDetails()
        {
            HostedServiceList hostedServiceList = GetHostedServices();

            HostedServiceList hostedServicesWithDetails = new HostedServiceList();


            System.TimeSpan diffResult = DateTime.Now.Subtract(ManagementContext.HostedServicesLastRefresh);
            if (diffResult.TotalSeconds > 30)
            {
                foreach (HostedService hostedService in hostedServiceList)
                {
                    HostedService hostedServiceWithDetails = GetHostedServiceWithDetails(hostedService.ServiceName);

                    hostedServicesWithDetails.Add(hostedServiceWithDetails);

                    ManagementContext.HostedServicesLastRefresh = DateTime.Now;
                }

                ManagementContext.HostedServices = hostedServicesWithDetails;
            }
            else
            {
                hostedServicesWithDetails = ManagementContext.HostedServices;
            }


            return hostedServicesWithDetails;
        }

        /// <summary>
        /// Get a list of deployments
        /// </summary>
        /// <returns></returns>
        public static DeploymentList GetDeployments()
        {
            HostedServiceList hostedServicesWithDetails = GetHostedServicesWithDetails();

            DeploymentList deploymentList = new DeploymentList();
            foreach (HostedService hostedService in hostedServicesWithDetails)
            {
                foreach (Deployment deployment in hostedService.Deployments)
                {
                    deploymentList.Add(deployment);
                }
            }

            return deploymentList;
        }

        /// <summary>
        /// Gets detailed info on a hosted service.
        /// </summary>
        /// <returns></returns>
        public static HostedService GetHostedServiceWithDetails(string serviceName)
        {
            OperationContext operationContext = new OperationContext("Get hosted service details for " + serviceName);
            ManagementContext.AddOperationContext(operationContext);

            var serviceManagementChannel = ServiceManagementHelper.CreateServiceManagementChannel("WindowsAzureEndPoint", ManagementContext.certificate);

            HostedService hostedServiceWithDetails = null;

            try
            {
                using (OperationContextScope scope = new OperationContextScope((IContextChannel)serviceManagementChannel))
                {
                    try
                    {
                        hostedServiceWithDetails = serviceManagementChannel.GetHostedServiceWithDetails(ManagementContext.SubscriptionId, serviceName, true);

                        if (WebOperationContext.Current.IncomingResponse != null)
                        {
                            operationContext.AddWebOperationContext(WebOperationContext.Current);
                        }

                        hostedServiceWithDetails.HostedServiceProperties.Label = ServiceManagementHelper.DecodeFromBase64String(hostedServiceWithDetails.HostedServiceProperties.Label);
                    }
                    catch (CommunicationException ce)
                    {
                        HandleCommunicationError(ce);
                    }
                }
            }
            catch (TimeoutException)
            {

            }


            return hostedServiceWithDetails;
        }

        /// <summary>
        /// Removes a hosted service info object for a specified service name.
        /// </summary>
        /// <returns></returns>
        public static void DeleteHostedService(string serviceName)
        {
            OperationContext operationContext = new OperationContext("Delete hosted service " + serviceName);
            ManagementContext.AddOperationContext(operationContext);

            var serviceManagementChannel = ServiceManagementHelper.CreateServiceManagementChannel("WindowsAzureEndPoint", ManagementContext.certificate);

            HostedService hostedService = GetHostedService(serviceName);

            try
            {
                using (OperationContextScope scope = new OperationContextScope((IContextChannel)serviceManagementChannel))
                {
                    ManagementContext.CurrentActionDescription = "Delete hosted service " + serviceName;
                    try
                    {
                        serviceManagementChannel.DeleteHostedService(ManagementContext.SubscriptionId, serviceName);
                        if (WebOperationContext.Current.IncomingResponse != null)
                        {
                            operationContext.AddWebOperationContext(WebOperationContext.Current);
                        }
                    }
                    catch (CommunicationException ce)
                    {
                        HandleCommunicationError(ce);
                    }
                }
            }
            catch (TimeoutException)
            {

            }
        }

        /// <summary>
        /// Removes a hosted service info object for a specified service label.
        /// </summary>
        /// <returns></returns>
        public static void DeleteHostedServiceByLabel(string serviceLabel)
        {
            var serviceManagementChannel = ServiceManagementHelper.CreateServiceManagementChannel("WindowsAzureEndPoint", ManagementContext.certificate);

            //TODO:Alert user of actions taken below! Manual check advised!
            //If collect hostes services by label we can expect to receive more than one. And we will delete all of them!
            HostedServiceList hostedServiceList = GetHostedServicesByLabel(serviceLabel);

            if (hostedServiceList == null) return;

            foreach (HostedService hostedService in hostedServiceList)
            {
                try
                {
                    using (OperationContextScope scope = new OperationContextScope((IContextChannel)serviceManagementChannel))
                    {
                        try
                        {
                            //All deployments need to be deleted before we can delete the hosted service.
                            List<string> blockingOperations = DeleteDeployments(hostedService);

                            bool deleteDeploymentsPending = true;

                            //Wait for the background worker to pickup the related responses (to the async calls) 
                            //and call the API to update their status. If all tracking id's in the list have a succeeded status
                            //we can continue the next calls.
                            //TODO:Make this event/callback based.
                            while (deleteDeploymentsPending)
                            {
                                deleteDeploymentsPending = false;
                                foreach (string trackingId in blockingOperations)
                                {
                                    var relatedResponse = from response in ManagementContext.Operations where response.TrackingId == trackingId select response;

                                    if (relatedResponse.Count() > 0)
                                    {
                                        if (!relatedResponse.Single().Completed)
                                            deleteDeploymentsPending = true;
                                    }
                                }
                            }

                            OperationContext operationContext = new OperationContext("Delete hosted service " + hostedService.ServiceName);
                            ManagementContext.AddOperationContext(operationContext);
                            serviceManagementChannel.DeleteHostedService(ManagementContext.SubscriptionId, hostedService.ServiceName);
                            if (WebOperationContext.Current.IncomingResponse != null)
                            {
                                operationContext.AddWebOperationContext(WebOperationContext.Current);
                            }
                        }
                        catch (CommunicationException ce)
                        {
                            HandleCommunicationError(ce);
                        }
                    }
                }
                catch (TimeoutException)
                {

                }
            }
        }

        /// <summary>
        /// Deletes all deployment under the specified hosted service. 
        /// </summary>
        /// <param name="hostedService">This needs to be a service host instance with details (embed-detail=true)</param>
        public static List<string> DeleteDeployments(HostedService hostedService)
        {
            var serviceManagementChannel = ServiceManagementHelper.CreateServiceManagementChannel("WindowsAzureEndPoint", ManagementContext.certificate);

            List<string> blockingOperations = new List<string>();

            if (hostedService.Deployments == null || hostedService.Deployments.Count == 0) return blockingOperations;


            foreach (Deployment deployment in hostedService.Deployments)
            {
                ////All deployments need to be suspended before they can be deleted.
                UpdateDeploymentStatusInput input = new UpdateDeploymentStatusInput
                {
                    Status = "suspended"
                };
                string blockingOperationId = UpdateDeploymentStatus(deployment.Name, hostedService.ServiceName, input);

                bool blockingOperationPending = true;

                //Wait for the background worker to pickup the related response (async call) 
                //and call the API to update the status. When the tracking gets a succeeded status
                //we can continue the next calls.
                ////TODO:Make this event/callback based.
                while (blockingOperationPending)
                {
                    blockingOperationPending = false;

                    var relatedResponse = from response in ManagementContext.Operations where response.TrackingId == blockingOperationId select response;

                    if (relatedResponse.Count() > 0)
                    {
                        if (!relatedResponse.Single().Completed)
                            blockingOperationPending = true;
                    }
                }

                using (OperationContextScope scope = new OperationContextScope((IContextChannel)serviceManagementChannel))
                {
                    OperationContext operationContext = new OperationContext("Delete deployment " + deployment.Name + " on service " + hostedService.ServiceName);
                    ManagementContext.AddOperationContext(operationContext);
                    try
                    {

                        serviceManagementChannel.DeleteDeployment(ManagementContext.SubscriptionId, hostedService.ServiceName, deployment.Name);
                        if (WebOperationContext.Current.IncomingResponse != null)
                        {
                            operationContext.AddWebOperationContext(WebOperationContext.Current);
                            //Since this is an async call we store the trackingId in a list so we can wait for it to complete
                            //and call GetOperationStatus to check it's status.
                            blockingOperations.Add(operationContext.TrackingId);
                        }
                    }
                    catch (CommunicationException ce)
                    {
                        HandleCommunicationError(ce);
                    }
                }
            }

            return blockingOperations;
        }

        /// <summary>
        /// Stops a deployment 
        /// </summary>
        public static string UpdateDeploymentStatus(string deploymentName, string serviceName, UpdateDeploymentStatusInput input)
        {
            OperationContext operationContext = new OperationContext("Stop deployment " + deploymentName);
            ManagementContext.AddOperationContext(operationContext);

            var serviceManagementChannel = ServiceManagementHelper.CreateServiceManagementChannel("WindowsAzureEndPoint", ManagementContext.certificate);

            string trackingId = null;

            using (OperationContextScope scope = new OperationContextScope((IContextChannel)serviceManagementChannel))
            {
                try
                {
                    serviceManagementChannel.UpdateDeploymentStatus(ManagementContext.SubscriptionId, serviceName, deploymentName, input);
                    if (WebOperationContext.Current.IncomingResponse != null)
                    {
                        operationContext.AddWebOperationContext(WebOperationContext.Current);

                        trackingId = operationContext.TrackingId;
                    }
                }
                catch (CommunicationException ce)
                {
                    HandleCommunicationError(ce);
                }
            }

            return trackingId;
        }

        /// <summary>
        /// Get all locations (the regions) currently available for the Foundation Services data centers.
        /// </summary>
        /// <returns></returns>
        public static LocationList GetLocations()
        {
            OperationContext operationContext = new OperationContext("List locations");
            ManagementContext.AddOperationContext(operationContext);

            var serviceManagementChannel = ServiceManagementHelper.CreateServiceManagementChannel("WindowsAzureEndPoint", ManagementContext.certificate);

            LocationList locationList = null;

            try
            {
                using (OperationContextScope scope = new OperationContextScope((IContextChannel)serviceManagementChannel))
                {
                    try
                    {
                        locationList = serviceManagementChannel.ListLocations(ManagementContext.SubscriptionId);
                        if (WebOperationContext.Current.IncomingResponse != null)
                        {
                            operationContext.AddWebOperationContext(WebOperationContext.Current);
                        }
                    }
                    catch (CommunicationException ce)
                    {
                        HandleCommunicationError(ce);
                    }
                }
            }
            catch (TimeoutException)
            {

            }

            return locationList;
        }

        public static Operation GetOperationStatus(string operationId)
        {
            var serviceManagementChannel = ServiceManagementHelper.CreateServiceManagementChannel("WindowsAzureEndPoint", ManagementContext.certificate);

            Operation operation = null;

            try
            {
                using (OperationContextScope scope = new OperationContextScope((IContextChannel)serviceManagementChannel))
                {
                    operation = serviceManagementChannel.GetOperationStatus(ManagementContext.SubscriptionId, operationId);
                }
            }
            catch (TimeoutException)
            {
            }

            return operation;
        }

        /// <summary>
        /// Create a hosted service via de API. Using an input class of type CreateHostedServiceInput.
        /// </summary>
        public static void CreateHostedService(CreateHostedServiceInput input)
        {
            //Remove the service if it exists. (Reason 1:Cleanup auto generated services, Reason 2:existing service names (in our case GUID's) are not in sync with a new deployment)
            DeleteHostedServiceByLabel(input.Label);

            var serviceManagementChannel = ServiceManagementHelper.CreateServiceManagementChannel("WindowsAzureEndPoint", ManagementContext.certificate);

            OperationContext operationContext = new OperationContext("Create hosted service in " + input.Location + " labeled " + input.Label);
            ManagementContext.AddOperationContext(operationContext);
            //Encode the label field, required by the API service.
            input.Label = ServiceManagementHelper.EncodeToBase64String(input.Label);

            try
            {
                using (OperationContextScope scope = new OperationContextScope((IContextChannel)serviceManagementChannel))
                {
                    try
                    {
                        serviceManagementChannel.CreateHostedService(input, ManagementContext.SubscriptionId);
                        if (WebOperationContext.Current.IncomingResponse != null)
                        {
                            operationContext.AddWebOperationContext(WebOperationContext.Current);
                        }
                    }
                    catch (CommunicationException ce)
                    {
                        HandleCommunicationError(ce);
                    }
                }
            }
            catch (TimeoutException)
            {

            }
        }

        /// <summary>
        /// Uploads a package to blob storage.
        /// </summary>
        public static void UploadPackageToBlobFromFile(Uri blobEndpoint, string accountName, string accountKey, string filePath, string containerName, string blobName, bool skipIfExists)
        {
            OperationContext operationContext = new OperationContext("Upload package to " + accountName + " under " + containerName + "/" + blobName);
            ManagementContext.AddOperationContext(operationContext);
            //Create service client for credentialed access to the Blob service.
            CloudBlobClient blobClient = new CloudBlobClient(blobEndpoint, new StorageCredentialsAccountAndKey(accountName, accountKey));

            try
            {
                //Get a reference to a container, which may or may not exist.
                CloudBlobContainer container = blobClient.GetContainerReference(containerName.ToLower());

                //Create a new container, if it does not exist
                container.CreateIfNotExist();

                //Get a reference to a blob, which may or may not exist.
                CloudBlob blob = container.GetBlobReference(blobName);

                if (blob.Properties.Length > 0 && skipIfExists) return;
                //Upload content to the blob, which will create the blob if it does not already exist.
                blob.UploadFile(filePath);


                operationContext.Completed = true;
                operationContext.StatusDescription = "Succeeded";
                operationContext.HttpStatusCode = HttpStatusCode.Accepted;
            }
            catch (Exception ex)
            {

                operationContext.CommunicationExceptionMessage = ex.Message;
                operationContext.Completed = false;
            }
        }

        /// <summary>
        /// Uses the cspack.exe tool from the Windows Azure SDK to generate a service package.
        /// </summary>
        /// <param name="serviceDefinitionFilePath">The path to the (generated) servicedefinition file</param>
        /// <param name="roleName">The name of the role as mentioned in de service definition (corresponds with the type in the roleEntryPoint dll that inherits from RoleEntryPoint</param>
        /// <param name="roleBinariesFolder">This is de location where all the binaries are collected from to be zipped together.</param>
        /// <param name="roleEntryPointDll">The assembly that needs to be executed as part of the role initializatio process</param>
        /// <param name="outputLocation">The location where the packege file should be stored</param>
        public static void CreateServicePackage(string serviceDefinitionFilePath, string roleName, string roleBinariesFolder, string roleEntryPointDll, string outputLocation)
        {
            string executingAssemblyLocation = ManagementContext.ExecutionAssemblyLocation + "\\Dependencies\\";

            string arguments = Path.Combine(executingAssemblyLocation, serviceDefinitionFilePath) + " /role:" + roleName + "; " + roleBinariesFolder + "; " + roleEntryPointDll + " /out:" + outputLocation;
            ProcessStarter.RunSilentProcess(Path.Combine(Assembly.GetExecutingAssembly().Location, "cspack.exe"), arguments);
        }

        /// <summary>
        /// Add a certificate to the hosted service. We need this certificate to manage the service throught the API.
        /// </summary>
        /// <param name="input"></param>
        /// <param name="serviceName"></param>
        /// <param name="deploymentSlot"></param>
        public static void AddCertificateToService(CertificateFile input, string serviceName)
        {
            var serviceManagementChannel = ServiceManagementHelper.CreateServiceManagementChannel("WindowsAzureEndPoint", ManagementContext.certificate);
            var certificateData = ManagementContext.certificate.Export(X509ContentType.Pfx, ManagementContext.CertificatePrivateKeyPassword);
            input.Data = Convert.ToBase64String(certificateData);

            OperationContext operationContext = new OperationContext("Add certificate to " + serviceName);
            ManagementContext.AddOperationContext(operationContext);
            try
            {
                using (OperationContextScope scope = new OperationContextScope((IContextChannel)serviceManagementChannel))
                {
                    try
                    {
                        serviceManagementChannel.AddCertificates(ManagementContext.SubscriptionId, serviceName, input);
                        if (WebOperationContext.Current.IncomingResponse != null)
                        {
                            operationContext.AddWebOperationContext(WebOperationContext.Current);
                        }
                    }
                    catch (CommunicationException ce)
                    {
                        HandleCommunicationError(ce);
                    }
                }
            }
            catch (TimeoutException)
            {
                ////TODO: Make some fancy moves here....
            }
        }

        /// <summary>
        /// Create a deployment of a package using the input type of CreateDeploymentInput.
        /// </summary>
        /// <param name="input"></param>
        /// <param name="serviceName"></param>
        /// <param name="deploymentSlot">production or staging</param>
        public static string CreateDeployment(CreateDeploymentInput input, string serviceName, string deploymentSlot)
        {
            OperationContext operationContext = new OperationContext("Create " + deploymentSlot + " deployment for " + input.Label);
            ManagementContext.AddOperationContext(operationContext);

            var serviceManagementChannel = ServiceManagementHelper.CreateServiceManagementChannel("WindowsAzureEndPoint", ManagementContext.certificate);

            input.Label = ServiceManagementHelper.EncodeToBase64String(input.Label);
            input.Configuration = ServiceManagementHelper.EncodeToBase64String(input.Configuration);


            //This is an async call so we need a trackingId to check the call status.
            string trackingId = null;


            try
            {
                using (OperationContextScope scope = new OperationContextScope((IContextChannel)serviceManagementChannel))
                {
                    try
                    {
                        serviceManagementChannel.CreateOrUpdateDeployment(ManagementContext.SubscriptionId, serviceName, deploymentSlot, input);

                        if (WebOperationContext.Current.IncomingResponse != null)
                        {
                            operationContext.AddWebOperationContext(WebOperationContext.Current);
                            trackingId = operationContext.TrackingId;
                        }
                    }
                    catch (CommunicationException ce)
                    {
                        HandleCommunicationError(ce);
                    }
                }
            }
            catch (TimeoutException)
            {

            }

            return trackingId;
        }

        /// <summary>
        /// Pulls info from the CommunicationException object and puts it a the responseInfo object.
        /// </summary>
        /// <param name="ce"></param>
        private static void HandleCommunicationError(CommunicationException ce)
        {
            OperationContext operationContext = new OperationContext(ManagementContext.CurrentActionDescription);
            ServiceManagementError error = null;
            HttpStatusCode httpStatusCode = 0;
            string operationId;
            ServiceManagementHelper.TryGetExceptionDetails(ce, out error, out httpStatusCode, out operationId);
            if (error == null)
            {
                operationContext.CommunicationExceptionMessage = string.Format(ce.Message);
            }
            else
            {
                operationContext.HttpStatusCode = httpStatusCode;
                operationContext.ErrorMessage = error.Message;
            }

            ManagementContext.AddOperationContext(operationContext);
        }
    }
}
